﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;

namespace VSCPPublishPlugin
{
	public static class HttpFormScraper
	{
		public static string[] ExtractViewStateFromAspNetFormByUri(Uri uri, params string[] fieldNames)
		{
			var webRequest = WebRequest.Create(uri) as HttpWebRequest;
			var responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
			var responseData = responseReader.ReadToEnd();
			responseReader.Close();
			var fieldValues = ExtractViewState(responseData, fieldNames);
			return fieldValues;
		}
		public static string[] ExtractViewState(string s, params string[] fieldNames)
		{
			var fieldValues = new string[fieldNames.Length];
			var i = 0;
			foreach (var fieldName in fieldNames)
			{
				const string valueDelimiter = "value=\"";

				var viewStateNamePosition = s.IndexOf(fieldName);
				var viewStateValuePosition = s.IndexOf(valueDelimiter, viewStateNamePosition);

				var viewStateStartPosition = viewStateValuePosition + valueDelimiter.Length;
				var viewStateEndPosition = s.IndexOf("\"", viewStateStartPosition);

				fieldValues[i++] = s.Substring(viewStateStartPosition, viewStateEndPosition - viewStateStartPosition);
			}

			return fieldValues;
		}

		public struct HtmlFormField_t
		{
			public HtmlFormField_t(string value, string additionalData = null)
			{
				_data = Encoding.UTF8.GetBytes(value);
				AdditionalData = additionalData;
			}
			public HtmlFormField_t(Stream stream, string additionalData = null)
			{
				if (!stream.CanRead)
					throw new InvalidOperationException("Parameter 'stream' must be a readable Stream.");

				var len = stream.Length;
				_data = new byte[len];
				stream.Read(_data, 0, (int)len);
				AdditionalData = additionalData;
			}
			public HtmlFormField_t(byte[] data, string additionalData = null)
			{
				if (null == data || 0 == data.Length)
					throw new InvalidOperationException("Parameter 'stream' must be a readable Stream.");

				_data = new byte[data.Length];
				data.CopyTo(_data, 0);
				AdditionalData = additionalData;
			}
			public string AdditionalData;
			internal byte[] _data;
		}

		private static void WriteFormField(Stream stream, string fieldBoundary, string name, HtmlFormField_t field)
		{
			var writer = new StreamWriter(stream);
			writer.Write("--");
			writer.Write(fieldBoundary);
			writer.Write("\r\n");
			writer.Write("Content-Disposition: form-data; name=\"" + name + "\"\r\n");
			if (!string.IsNullOrEmpty(field.AdditionalData))
			{
				writer.Write(field.AdditionalData);
				writer.Write("\r\n");
			}
			writer.Write("\r\n");
			writer.Flush();

			if (null != field._data && 0 != field._data.Length)
			{

				var bwriter = new BinaryWriter(stream);
				bwriter.Write(field._data);
				bwriter.Flush();
			}

			writer.Write("\r\n");
			writer.Flush();
		}

		public static void PostHttpForm(HttpWebRequest webRequest, Dictionary<string, HtmlFormField_t> additionalParams, out string response)
		{
			HttpWebResponse wResponse;
			PostHttpForm(webRequest, additionalParams, out wResponse, out response);
		}
		public static void PostHttpForm(HttpWebRequest webRequest, Dictionary<string, HtmlFormField_t> additionalParams, out HttpWebResponse webResponse, out string response)
		{
			var formBoundary = "---------------------------" + DateTime.Now.ToBinary();

			var stream = new MemoryStream();
			foreach (var param in additionalParams)
			{
				WriteFormField(stream, formBoundary, param.Key, param.Value);
			}
			var writer = new StreamWriter(stream);
			writer.Write("--" + formBoundary + "--\r\n");
			writer.Flush();

			var len = stream.Length;


			// now post to the login form
			webRequest.Method = "POST";
			webRequest.ContentType = "multipart/form-data; boundary=" + formBoundary;
			webRequest.ContentLength = len;
			webRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8";
			webRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
			webRequest.Referer = webRequest.RequestUri.AbsoluteUri;


			// write the form values into the request message
			stream.Position = 0;
			var reqWriter = new BinaryWriter(webRequest.GetRequestStream());
			WriteStreamToStreamWriter(stream, reqWriter);
			reqWriter.Close();

			webResponse = (HttpWebResponse)webRequest.GetResponse();
			var responseReader = new StreamReader(webResponse.GetResponseStream());

			// and read the response
			var responseData = responseReader.ReadToEnd();
			responseReader.Close();

			response = responseData;
		}
		private static void WriteStreamToStreamWriter(Stream source, BinaryWriter destination, int bufferSize = 10000)
		{
			int num;
			var buffer = new byte[bufferSize];
			while ((num = source.Read(buffer, 0, buffer.Length)) != 0)
			{
				destination.Write(buffer, 0, num);
			}

		}
		public static void PostHttpForm(string uri, Dictionary<string, HtmlFormField_t> additionalParams, out string response, ref CookieContainer cookies)
		{
			HttpWebRequest webRequest;
			webRequest = WebRequest.Create(uri) as HttpWebRequest;
			webRequest.CookieContainer = cookies;

			PostHttpForm(webRequest, additionalParams, out response);
		}
	}
}
